package yunplus.io.hchome.model.api.implement;

import android.support.annotation.NonNull;
import android.util.Log;

import com.alibaba.fastjson.JSONObject;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;

import okhttp3.OkHttpClient;
import okhttp3.Protocol;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
import okio.ByteString;
import yunplus.io.hchome.model.api.contract.IFPMCallback;
import yunplus.io.hchome.model.api.contract.IWS;
import yunplus.io.hchome.model.api.contract.IWSInput;
import yunplus.io.hchome.utils.HandlerUtils;

/**
 * Created by WangFan on 2017/1/6.
 */

public class FPMWS extends WebSocketListener implements IWS {

    private final static String TAG = "FPMWS";

    private final static int RETRY_TIMES = 2;

    public FPMWS(OkHttpClient client){
        _client = client;
    }

    private final Map<Integer, IFPMCallback> _callbacks = new HashMap<Integer, IFPMCallback>();

    private OkHttpClient _client;

    private WebSocket _webSocket;

    private int _timeout = 1000;

    private String _wsUrl;

    private int _retryCount = 1;

    private int _sendCount = 1;

    private IFPMCallback _errorHandler;

    private boolean _isConnecting = false;

    private IFPMCallback _initHandler;

    private boolean _initHandlerCalled = false;

    @Override
    public void init(String wsUrl, String[] protocols) {
        Request _request = new Request.Builder()
                .url(wsUrl)
                .build();
        OkHttpClient.Builder _builder = _client.newBuilder();
        List<Protocol> _protocols = new ArrayList<Protocol>(protocols.length);
        for(String p : protocols){
            _protocols.add(Protocol.valueOf(p));
        }
        _builder.protocols(_protocols).build().newWebSocket(_request, this);

        Log.d(TAG, "init,url:" + wsUrl);
    }

    @Override
    public void init(String wsUrl) {
        this._wsUrl = wsUrl;
        this._retryCount = 1;
        reConnect(wsUrl);
    }

    @Override
    public void init(String wsUrl, IFPMCallback initHandler) {
        this._wsUrl = wsUrl;
        this._retryCount = 1;
        this._initHandler = initHandler;
        reConnect(wsUrl);
    }

    public void reConnect(String wsUrl){
        _retryCount++;
        if(_retryCount > RETRY_TIMES){
            if(this._errorHandler != null){
                this._errorHandler.onFailure("-10001", "ws cant connect");
            }
            return;
        }
        Request _request = new Request.Builder()
                .url(wsUrl)
                .build();
        _client.newWebSocket(_request, this);
    }

    @Override
    public void sendWS(@NonNull final IWSInput input, final IFPMCallback callback) {
        if(!_isConnecting){
            if(this._sendCount++ < RETRY_TIMES){
                HandlerUtils.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        FPMWS.this.sendWS(input,callback);
                    }
                }, _timeout);
            }else{
                callback.onFailure("-9999", "网络服务无法访问,请查看您的设备网关是否工作正常");
            }
        }else {
            this._sendCount = 1;
            _callbacks.put(input.getId(), callback);
            Log.d(TAG, input.toJson());
            _webSocket.send(input.toJson());
        }
    }

    @Override
    public void onOpen(WebSocket webSocket, Response response) {
        super.onOpen(webSocket, response);
        Log.d(TAG, "onOpen");
        _isConnecting = true;
        _webSocket = webSocket;
        if(_initHandler != null){
            _initHandler.onSuccess(null);
            _initHandler = null;
            _initHandlerCalled = true;
        }
    }



    @Override
    public void onMessage(WebSocket webSocket, String text) {
        super.onMessage(webSocket, text);
        Log.d(TAG, text);
        JSONObject object = JSONObject.parseObject(text);
        if(object.containsKey("id")){
            Integer id = object.getInteger("id");
            if(_callbacks.containsKey(id)){
                IFPMCallback cb = _callbacks.remove(id);
                //TODO: check the result status code;
                if(object.containsKey("status")){
                    Integer status = object.getInteger("status");
                    if(status == 0){
                        cb.onSuccess(object);
                    }else{
                        cb.onFailure("" + status, object.toJSONString());
                    }
                }else{
                    cb.onSuccess(object);
                }
            }
        }else{
            // not event
        }

    }

    @Override
    public void onMessage(WebSocket webSocket, ByteString bytes) {
        super.onMessage(webSocket, bytes);
    }

    @Override
    public void onClosing(WebSocket webSocket, int code, String reason) {
        super.onClosing(webSocket, code, reason);
        _isConnecting = false;
    }

    @Override
    public void onClosed(WebSocket webSocket, int code, String reason) {
        super.onClosed(webSocket, code, reason);
        _isConnecting = false;
        Log.d(TAG, "onClosed");
    }

    @Override
    public void onFailure(WebSocket webSocket, Throwable t, Response response) {
        super.onFailure(webSocket, t, response);
        if(_initHandler != null && !_initHandlerCalled){
            _initHandler.onFailure("9999", "无法连接网关服务");
        }
        _isConnecting = false;
        Log.e(TAG, "onFailure");
        Log.e(TAG, t.getMessage());
        String error = null;
        try {
            error = response.body().string();
        } catch (IOException e) {
            Log.e(TAG, "error:" + e.getMessage());
        }
        Log.e(TAG, "error:" + error);
        reConnect(this._wsUrl);
    }

    @Override
    public String getConnectedURL() {
        return _wsUrl;
    }

    @Override
    public boolean isConnecting() {
        return _isConnecting;
    }

    @Override
    public void onError(IFPMCallback callback) {
        this._errorHandler = callback;
    }
}
